/**
 * 
 */
package test;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Queue;
import java.util.Random;
import java.util.Timer;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * Automated testclient
 * @author Gernot
 *
 */
public class LoadTestClient extends Thread {
   private static int INITIAL_DELAY = 3000;
   private int auction_delay; // 60/auctionsPerMin *1000 = ms between auctions;
   private int auctionDuration;
   private int updateInterval; //updateIntervalSec*1000 
   private int bids_delay; // 60/bidsPerMin *1000 = ms between bids;
   private String host;
   private int port;
   private String name;
   private Socket s;
   private BufferedWriter wr;
   private OutputStreamWriter osr;
   private BufferedReader br;
   private InputStreamReader isr;
   private Timer timer = new Timer(); //timer to execute actions
   private Queue<String> responses = new ConcurrentLinkedQueue<String>();
   private Date start = new Date();
   private ArrayList<Integer> auctions = new ArrayList<Integer>();
   private long auctioncounter = 0L;
   private Random rnd = new Random(this.getId());
   private boolean running;
   
public LoadTestClient(String host, int port, String clientName, int auctionsPerMin, int auctionDuration,
        int updateIntervalSec, int bidsPerMin) {
    super();
    this.host = host;
    this.port = port;
    this.name = clientName;
    this.auction_delay = 60/auctionsPerMin *1000;
    this.auctionDuration = auctionDuration;
    this.updateInterval = updateIntervalSec*1000;
    this.bids_delay = 60/bidsPerMin*1000;
}


public void setRunning(boolean running) {
    this.running = running;
}




@Override
public void run() {
    //init client, login etc...
       
    try {
        s = new Socket(host, port);
        osr = new OutputStreamWriter(s.getOutputStream());
        isr = new InputStreamReader(s.getInputStream());
        wr = new BufferedWriter(osr);
        br = new BufferedReader(isr);
        
        //login user, //Assume it works
        this.sendTCPMessage("!login "+name+" 5000"); //TCP-port not used in LAb2, but required from login server
        
        String response = br.readLine();
        
        if (response.startsWith("Successfully logged in as")){
        
            //start listener Thread
            LoadTestClientTCPListener listener = new LoadTestClientTCPListener(this, br);
            listener.start();
            
            
            //Schedule actions
            timer.scheduleAtFixedRate(new TaskListAuctions(this), INITIAL_DELAY, this.updateInterval);
            timer.scheduleAtFixedRate(new TaskCreateAuction(this), INITIAL_DELAY, this.auction_delay);
            timer.scheduleAtFixedRate(new TaskBidAuction(this), INITIAL_DELAY, this.bids_delay);
            this.running = true;
            while (running){
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    
                }
            }
            timer.cancel();
            s.close();
        } else {
            s.close();
            System.out.println("ERROR: login failed, Test client terminated: Received:|"+response+"|");
        } 
        
    } catch (UnknownHostException e) {
        System.out.println(e.getMessage());
    } catch (IOException e) {
        System.out.println(e.getMessage());
    } finally {
        System.out.println("LoadTestClient: "+name+" ended.");
    }
}
 
 private void sendTCPMessage(String msg){
     try {
        wr.write(msg);
        wr.write("\n");
        wr.flush();
    } catch (IOException e) {
        System.out.println(e.getMessage());
    }
 }

 public void addToQueue(String s){
     synchronized(responses){
         this.responses.add(s);
     }     
 }
 
 public void createNewAuction(){
     //System.out.println("Creating new Auction");
      //!create 25200 Super small notebook
     String cmd = "!create "+ this.auctionDuration+ " Auction_"+name+"_"+this.auctioncounter++;
     this.sendTCPMessage(cmd);     
 }
 
 public void bidrandomAuction(){
     //!bid 3 250.00
     //System.out.println("Bid on random auction");
     this.parseQueue();
     int id = getAuctionId();
     if (id != -1) {
         double price = getBidPrice();
         //System.out.println("Bidding on auction "+id +" with "+price);
         this.sendTCPMessage("!bid "+id+" "+price);
     }
 }
 
 
 public void parseQueue(){
     synchronized(responses){
         
        boolean newList = true;
        while (!responses.isEmpty()){
            String s = responses.poll();
            if (isAuctionListEntry(s)){
                if (newList){
                    this.clearAuctions();
                    newList=false;
                }
                int id = parseAuctionId(s);
                this.addAuctionId(id);
            } else {
                newList = true; 
            }
        }
       // debug_printAuctions();
    }
 }

 private void clearAuctions(){
     synchronized(auctions){
         auctions.clear();
     }
 }
 
 private int getAuctionId(){
     synchronized(auctions){
         int max = auctions.size()-1;
         if (max >=1){
             int i = rnd.nextInt(max);
             //System.out.println("Bid on Auction:"+auctions.get(i));
             return auctions.get(i);
         }else {
             return -1;
         }
     }
 }
 
 private void addAuctionId(Integer id){
     synchronized(auctions){
         auctions.add(id);
     }
 }
 
 
 private double getBidPrice(){
     return (System.currentTimeMillis()-start.getTime())/1000;
 }
 
 /*private String getResponse(){
    try {
        String response = br.readLine();
        return response;
    } catch (IOException e) {
        System.out.println(e.getMessage());
        return "";
    }
 }*/
 
public void updateAuctions(){
    //System.out.println("List auctions");
    this.sendTCPMessage("!list");
}

private static int parseAuctionId(String s){
    String[] str = s.split("\\."); 
    if (str.length > 1){
        try {
            int id = Integer.parseInt(str[0]);
            return id;
        } catch (NumberFormatException e) {
            return -1;
        }
    }
    return -1;
}

/**
 * returns if s is a List-entry
 * @param s
 * @return
 */
private static boolean isAuctionListEntry(String s){
    return s.matches("\\d+\\. .*"); //Starting with <number>.<space> should be enough to identify list entries
}

/*private void debug_printAuctions(){
    System.out.print("Auction ID's: ");
    for (Integer i : this.auctions){
        System.out.print(i+", ");
    }
    System.out.println();
}*/
   
}
